package com.bytz.modules.cms.auditBatch.service.change;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bytz.common.constant.CommonConstant;
import com.bytz.common.exception.BytzBootException;
import com.bytz.common.util.RedisUtil;
import com.bytz.modules.cms.auditBatch.service.IChangeService;
import com.bytz.modules.cms.common.constants.CmsConstant;
import com.bytz.modules.cms.common.model.CategoryDiscountSearchVo;
import com.bytz.modules.cms.common.util.CmsCacheKeyUtil;
import com.bytz.modules.cms.product.entity.ProductDiscountCategory;
import com.bytz.modules.cms.product.entity.ProductDiscountCategoryLevel;
import com.bytz.modules.cms.product.entity.ProductDiscountCategoryLevelChange;
import com.bytz.modules.cms.product.mapper.ProductDiscountCategoryLevelChangeMapper;
import com.bytz.modules.cms.product.service.IProductDiscountCategoryLevelService;
import com.bytz.modules.cms.product.service.IProductDiscountCategoryService;
import com.bytz.modules.cms.productLine.entity.ProductLine;
import com.bytz.modules.cms.productLine.entity.ProductLineLevel;
import com.bytz.modules.cms.productLine.service.IProductLineLevelService;
import com.bytz.modules.cms.productLine.service.IProductLineService;
import com.bytz.modules.cms.user.constants.AuditBatchConstants;
import com.bytz.modules.cms.user.service.IAuditBatchService;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class BatchProductCategoryChangeServiceImpl extends ServiceImpl<ProductDiscountCategoryLevelChangeMapper, ProductDiscountCategoryLevelChange> implements IChangeService<ProductDiscountCategoryLevelChange> {

    @Autowired
    private IProductDiscountCategoryLevelService categoryLevelService;

    @Autowired
    private IProductDiscountCategoryService categoryService;

    @Autowired
    private IProductLineLevelService lineLevelService;

    @Autowired
    private IAuditBatchService auditBatchService;

    @Autowired
    private IProductLineService productLineService;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncChange(String batchId) {

        List<ProductDiscountCategoryLevelChange> changeList = setDiscountCategoryLevelId(batchId);

        if (CollectionUtil.isEmpty(changeList)) {
            return;
        }

        Map<String, List<ProductDiscountCategoryLevelChange>> idGroup = changeList.stream()
                .collect(Collectors.groupingBy(ProductDiscountCategoryLevelChange::getDiscountCategoryLevelId));

        List<ProductDiscountCategoryLevel> updateList = idGroup.entrySet().stream().map(entry -> {
            ProductDiscountCategoryLevel updateEntity = new ProductDiscountCategoryLevel();
            updateEntity.setId(entry.getKey());
            entry.getValue().stream().findFirst().ifPresent(categoryLevel -> {
                updateEntity.setDiscountRatio(categoryLevel.getDiscountRatio());
                updateEntity.setEffectTime(LocalDateTime.now());
            });
            return updateEntity;
        }).collect(Collectors.toList());

        updateSyncData(batchId, updateList);

        // 清除计算价格时用的产品分类折扣缓存
        Set<String> keys = updateList.stream()
                .map(level -> {
                    CategoryDiscountSearchVo searchVo = new CategoryDiscountSearchVo();
                    searchVo.setProductCategoryId(level.getDiscountCategoryId());
                    searchVo.setProductLineLevelId(level.getProductLineLevelId());
                    return CmsCacheKeyUtil.getProductCategoryDiscountKey(CmsConstant.CachePrefixes.PRODUCT_CATEGORY_DISCOUNT, searchVo);
                })
                .collect(Collectors.toSet());

        redisUtil.del(keys);
    }

    /**
     * 获取主表id，并保存到change表中
     * 主表不存在相应数据的，给主表中添加一条
     */
    private List<ProductDiscountCategoryLevelChange> setDiscountCategoryLevelId(String batchId) {
        // 填入主表id
        MPJLambdaWrapper<ProductDiscountCategoryLevelChange> wrapper = new MPJLambdaWrapper<>();
        List<ProductDiscountCategoryLevelChange> result = baseMapper.selectJoinList(ProductDiscountCategoryLevelChange.class, wrapper
                .selectAll(ProductDiscountCategoryLevelChange.class)
                .selectAs(ProductDiscountCategoryLevel::getId, ProductDiscountCategoryLevelChange::getDiscountCategoryLevelId)
                .leftJoin(ProductDiscountCategoryLevel.class, on -> on
                        .eq(ProductDiscountCategoryLevelChange::getDiscountCategoryId, ProductDiscountCategoryLevel::getDiscountCategoryId)
                        .eq(ProductDiscountCategoryLevelChange::getProductLineLevelId, ProductDiscountCategoryLevel::getProductLineLevelId)
                        .eq(ProductDiscountCategoryLevel::getDelFlag, CommonConstant.DEL_FLAG_0)
                )
                .eq(ProductDiscountCategoryLevelChange::getAuditBatchId, batchId)
                .eq(ProductDiscountCategoryLevelChange::getSyncStatus, AuditBatchConstants.SyncStatus.WAITING_SYNC)
        );

        List<ProductDiscountCategoryLevel> insertList = result.stream()
                .filter(change -> change.getDiscountCategoryLevelId() == null)
                .map(change -> {
                    // 如果主表中没有分类等级都对应的记录，添加一条
                    ProductDiscountCategoryLevel insertLevel = new ProductDiscountCategoryLevel();
                    insertLevel.setDiscountCategoryId(change.getDiscountCategoryId());
                    insertLevel.setDiscountCategoryName(change.getDiscountCategoryName());
                    insertLevel.setProductLineLevelId(change.getProductLineLevelId());
                    insertLevel.setProductLineLevelName(change.getProductLineLevelName());
                    insertLevel.setDiscountRatio(BigDecimal.ONE);
                    insertLevel.setEffectTime(LocalDateTime.now());
                    return insertLevel;
                }).collect(Collectors.toList());

        if (CollectionUtil.isNotEmpty(insertList)) {

            categoryLevelService.saveBatch(insertList);

            Map<String, String> insertMap = insertList.stream()
                    .collect(Collectors.toMap(t -> t.getDiscountCategoryId() + t.getProductLineLevelId(), ProductDiscountCategoryLevel::getId));

            result.forEach(change -> {
                String id = insertMap.get(change.getDiscountCategoryId() + change.getProductLineLevelId());
                if (id != null) {
                    change.setDiscountCategoryLevelId(id);
                }
            });
        }

        updateBatchById(result);

        return result;
    }

    /**
     * 更改auditBatch表 将对应id设置为已同步
     * 更改change表 对应batchId设置为已同步
     * 更新对应的主表
     */
    private void updateSyncData(String batchId, List<ProductDiscountCategoryLevel> updateList) {

        categoryLevelService.updateBatchById(updateList);

        lambdaUpdate()
                .eq(ProductDiscountCategoryLevelChange::getAuditBatchId, batchId)
                .set(ProductDiscountCategoryLevelChange::getSyncStatus, AuditBatchConstants.SyncStatus.SYNCED)
                .update();

        auditBatchService.setStatusSynced(batchId);
    }

    @Override
    public List<ProductDiscountCategoryLevelChange> getByBatchId(String batchId) {
        return lambdaQuery()
                .eq(ProductDiscountCategoryLevelChange::getAuditBatchId, batchId)
                .list();
    }

    @Override
    public void removeByBatchId(String batchId) {
        lambdaUpdate().eq(ProductDiscountCategoryLevelChange::getAuditBatchId, batchId).remove();
    }

    @Override
    public SFunction<ProductDiscountCategoryLevelChange, String> getKeyExtractor() {
        return ProductDiscountCategoryLevelChange::getDiscountCategoryLevelId;
    }

    @Override
    public List<ProductDiscountCategoryLevelChange> importExcel(List<ProductDiscountCategoryLevelChange> models) {

        Set<String> categoryNames = models.stream().map(ProductDiscountCategoryLevelChange::getDiscountCategoryName).collect(Collectors.toSet());

        // 只能导入自己产品线的分类
        String lineId = productLineService.getLoginUserLine().map(ProductLine::getId).orElse(null);
        Map<String, ProductDiscountCategory> categoryNameMap = categoryService.getByNamesAndProductLine(categoryNames, lineId)
                .stream().collect(Collectors.toMap(ProductDiscountCategory::getCategoryName, t -> t));

        Set<String> lineLevelNames = models.stream().map(ProductDiscountCategoryLevelChange::getProductLineLevelName).collect(Collectors.toSet());
        Map<String, ProductLineLevel> levelNameMap = lineLevelService.getByNames(lineLevelNames)
                .stream().collect(Collectors.toMap(ProductLineLevel::getLevelName, t -> t));

        models.forEach(change -> {
            ProductDiscountCategory category = categoryNameMap.get(change.getDiscountCategoryName());
            if (category == null) {
                throw new BytzBootException(String.format("找不到【%s】对应的优惠分类", change.getDiscountCategoryName()));
            }
            change.setDiscountCategoryId(category.getId());

            ProductLineLevel productLineLevel = levelNameMap.get(change.getProductLineLevelName());
            if (productLineLevel == null) {
                throw new BytzBootException(String.format("找不到【%s】对应的等级", change.getProductLineLevelName()));
            }
            change.setProductLineLevelId(productLineLevel.getId());

            change.setDiscountRatio(change.getDiscountRatio().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP));
        });
        return models;
    }
}
